
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <assert.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <stdlib.h>
#include <sys/epoll.h>
#include <signal.h>
#include <sys/wait.h>
#include <sys/stat.h>

#include "../ProcessPool/processpool.hpp"

using namespace std;

class CgiConnect
{
public:
    CgiConnect() {}
    ~CgiConnect() {}

    void init(int _epollfd,int _sockfd,const sockaddr_in& _cliend_address)
    {
        m_epollfd = _epollfd;
        m_sockfd = _sockfd;
        m_client_address = _cliend_address;
        memset(m_buf,'\0',BUFFER_SIZE);
        m_read_idx = 0;
    }

    void process()
    {
        int idx = 0;
        int ret = -1;
        // 分析客户端传递的数据
        while (true)
        {
            idx = m_read_idx;
            ret = recv(m_sockfd,m_buf,BUFFER_SIZE - 1 - idx,0);
            if(ret < 0)
            {
                if(errno != EAGAIN) // 如果错误并不是没有数据可读，那么就退出连接
                {
                    removefd(m_epollfd,m_sockfd);
                }
                break;
            }else if(ret == 0)  // 客户端关闭连接
            {
                removefd(m_epollfd,m_sockfd);
                break;
            }else
            {
                m_read_idx += ret;
                printf("user content = [%s]\n",m_buf);
                for(;idx<m_read_idx;++idx)
                {
                    if((idx>=1) && (m_buf[idx-1] == '\r') && (m_buf[idx] == '\n') )
                    {
                        break;
                    }
                }
                // 读取到字符串末尾，没有检测到‘\t\n’
                if(idx == m_read_idx)
                {
                    continue;
                }
                m_buf[idx-1] = '\0';
                char* file_name = m_buf;
                // 检测CGI程序，失败就关闭客户端连接
                if(access(file_name,F_OK) == -1)
                {
                    removefd(m_epollfd,m_sockfd);
                    break;
                }
                // 创建一个子进程用于执行CGI程序
                ret = fork();
                if(ret == -1)   // 创建失败
                {
                    removefd(m_epollfd,m_sockfd);
                    break;
                }else if(ret > 0)   // 父线程
                {
                    removefd(m_epollfd,m_sockfd);
                    break;
                }else{              // 子线程
                    close(STDOUT_FILENO);
                    dup(m_sockfd);
                    execl(m_buf,m_buf,0);
                    exit(0);
                }
            }
        }
    }
private:
    static const int BUFFER_SIZE = 1024;
    static int m_epollfd;
    int m_sockfd;
    sockaddr_in m_client_address;
    char m_buf[BUFFER_SIZE];
    int m_read_idx;
};

int CgiConnect::m_epollfd = -1;

int main(int argc, char const *argv[])
{
    if(argc <= 2)
    {
        printf("usage:%s ip_address port_number\n",basename(argv[0]));
        return 1;
    }
    const char* ip = argv[1];
    int port = atoi(argv[2]);

    int listenfd = socket(PF_INET,SOCK_STREAM,0);
    assert(listenfd >= 0);

    int ret = 0;
    sockaddr_in address;
    bzero(&address,sizeof(address));
    address.sin_family = AF_INET;
    address.sin_port = htons(port);
    inet_pton(AF_INET,ip,&address.sin_addr);

    ret = bind(listenfd,(sockaddr*)&address,sizeof(address));
    assert(ret != -1);

    ret = listen(listenfd,5);
    assert(ret != -1);

    processpool<CgiConnect>* pool = processpool<CgiConnect>::create(listenfd);
    if(pool)
    {
        pool->run();
        delete pool;
    }
    close(listenfd);    // 遵循谁打开谁关闭的原则
    return 0;
}
